package com.woniuxy.portal.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.woniuxy.portal.entity.Book;
import com.woniuxy.portal.mapper.BookMapper;
import com.woniuxy.portal.service.IBookService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.Field;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author 作者
 * @since 2023-09-15
 */
@Service
public class BookServiceImpl extends ServiceImpl<BookMapper, Book> implements IBookService {

    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Override
    public List<Book> top10List() {
        // 参数1：分页对象
        Page<Book> page = Page.of(1, 10);
        // 参数2：构建查询对象进行排序
        // SELECT id,NAME,price,imgsrc FROM t_book ORDER BY buycount DESC LIMIT 10
        LambdaQueryWrapper<Book> wrapper = Wrappers.lambdaQuery(Book.class);
        // 查询指定的列, 默认是 select * , 开发中要避免这样用，效率低。
        wrapper.select(Book::getId,Book::getName,Book::getPrice,Book::getImgsrc);
        // 排序
        wrapper.orderByDesc(Book::getBuycount);
        Page<Book> pageData = this.page(page,wrapper);
        // 返回分页数据
        return pageData.getRecords();
    }

    /**
     * @Cacheable
     * 1、修饰在方法上，表示此方法返回结果时候缓存。
     * 2、执行方法之前，先查询redis缓存是否有数据，有数据直接放回；否则执行方法，执行完自动把方法返回结果放入Redis。
     * 3、修改的方法，返回的对象，默认情况，一定要实现Serializable序列化接口
     */
    @Cacheable(cacheNames = "books",key = "#typeId + '_' + #pageNum + '_' + #pageSize")
    @Override
    public IPage<Book> findByType(Integer typeId, Integer pageNum, Integer pageSize) {
        Page<Book> page = Page.of(pageNum, pageSize);
        LambdaQueryWrapper<Book> wrapper = Wrappers.lambdaQuery(Book.class);
        //SELECT id,NAME,price,imrsrc FROM t_book
        wrapper.select(Book::getId,Book::getName,Book::getPrice,Book::getImgsrc);
        //SELECT id,NAME,price,imrsrc FROM t_book where typeId=1
        // 参数1：条件表达式，只有条件表达式为true，才会执行条件查询
        wrapper.eq(typeId != null, Book::getTypeId, typeId);

        Page<Book> pageData = this.page(page, wrapper);
        return pageData;
    }

    @Override
    public Map<String, Object> search(String keyword, Integer pageNum, Integer pageSize) {

        // 条件构造器
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
        builder.withQuery(QueryBuilders.multiMatchQuery(keyword,"name","provider","author","detail"));
        builder.withPageable(PageRequest.of(pageNum-1,pageSize));

        // 设置高亮
        HighlightBuilder.Field[] fields = new HighlightBuilder.Field[1];
        fields[0] = new HighlightBuilder.Field("name")
                .preTags("<span style='color:red'>")
                .postTags("</span>");
        builder.withHighlightFields(fields);

        // 执行搜索
        SearchHits<Book> searchHits = elasticsearchRestTemplate.search(builder.build(), Book.class);
        // 处理结果
        List<Book> bookList = searchHits.getSearchHits().stream().map(searchHit -> {
            Book book = searchHit.getContent();

            // 获取高亮字段
            List<String> stringList = searchHit.getHighlightField("name");
            if (!CollectionUtils.isEmpty(stringList)) {
                StringBuffer sb = new StringBuffer();
                for (String str : stringList) {
                    sb.append(str);
                }
                book.setName(sb.toString()); // 设置高亮结果
            }

            return book;
        }).collect(Collectors.toList());

        // 返回结果
        Map<String,Object> map = new HashMap<>();
        map.put("list",bookList);
        map.put("total",searchHits.getTotalHits());
        return map;
    }
}
